package kernel.web;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import kernel.dao.HibernateUtils;
import kernel.util.StringUtils;

import org.apache.commons.beanutils.MethodUtils;
import org.hibernate.Query;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

public class PagedQueryDaoImpl extends HibernateDaoSupport implements PagedQueryDao {

    public Page pagedQuery(int pageNo, int pageSize, String queryString, Map<String, Object> parameters) {
        Page page = null;
        if (null != queryString) {
            String countString = " select count(*) "
                    + HibernateUtils.removeSelect(HibernateUtils.removeOrders(queryString));
            Query query = getSession(false).createQuery(countString);
            HibernateUtils.applyParameters(query, parameters);
            int totalCount = Integer.parseInt(((query.list()).get(0)).toString());
            if (totalCount == 0) {
                page = Page.EMPTY_PAGE;
            }
            else {
                page = new Page(pageNo, pageSize, totalCount);
                query = getSession(false).createQuery(queryString);
                HibernateUtils.applyParameters(query, parameters);
                query.setFirstResult(page.getFirstElementNumber());
                query.setMaxResults(pageSize);
                @SuppressWarnings("rawtypes")
                List list = query.list();
                page.setElements(list);
            }
        }
        return page;
    }

    private static final String TIMESTAMP_VALUE = "timestampValue";

    private static final String ORACLE_SQL_TIMESTAMP = "oracle.sql.TIMESTAMP";

    private NamedParameterJdbcOperations namedParameterJdbcTemplate;

    public Page pagedQuerySQL(int pageNo, int pageSize, String queryString) {
        return this.pagedQuerySQL(pageNo, pageSize, queryString, null, new LinkedHashMap<String, Object>());
    }

    public Page pagedQuerySQL(int pageNo, int pageSize, String queryString, Map<String, Object> searchParameter) {
        return this.pagedQuerySQL(pageNo, pageSize, queryString, null, searchParameter);
    }

    // 获取记录总数需改造，分页性能及健壮性可以继续优化
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Page pagedQuerySQL(int pageNo, int pageSize, String queryString, String countString,
            Map<String, Object> searchParameter) {
        Assert.notNull(queryString);
        Page page = null;
        if (StringUtils.isNullOrEmpty(countString)) {
            countString = " select count(*) " + HibernateUtils.removeSelect(HibernateUtils.removeOrders(queryString));
        }
        else {
            countString = countString + " " + HibernateUtils.removeSelect(HibernateUtils.removeOrders(queryString));
        }
        SqlParameterSource sqlParameterSource = applyParameters(searchParameter);
        int totalCount = namedParameterJdbcTemplate.queryForInt(countString, sqlParameterSource);
        if (totalCount == 0) {
            page = new Page();
        }
        else {
            SqlRowSet rs;
            page = new Page(pageNo, pageSize, totalCount);
            rs = namedParameterJdbcTemplate.queryForRowSet(queryString, sqlParameterSource);
            int startIndex = page.getFirstElementNumber();
            List list = new ArrayList();
            if (startIndex != 0) {
                rs.absolute(startIndex);
            }
            SqlRowSetMetaData rsmd = rs.getMetaData();
            Object[] rsArray;
            for (int count = 0; count < pageSize && rs.next(); count++) {
                rsArray = new Object[rsmd.getColumnCount()];
                for (int i = 1; i < rsmd.getColumnCount() + 1; i++) {
                    Object obj = rs.getObject(i);
                    if (obj != null && obj.getClass().getName().equals(ORACLE_SQL_TIMESTAMP)) {
                        try {
                            obj = MethodUtils.invokeMethod(obj, TIMESTAMP_VALUE, null);
                        } catch (Throwable e) {
                            e.printStackTrace();
                        }
                    }
                    rsArray[i - 1] = obj;
                }
                list.add(rsArray);
            }
            
            page.setElements(list);
            List elements = page.getElements();
            String select_column = queryString.substring(6, queryString.toLowerCase().indexOf("from"));;
            elements =  this.resultRefactor(elements, select_column);
            page.setElements(elements);
        }
        return page;
    }

    private SqlParameterSource applyParameters(Map<String, Object> parameters) {
        MapSqlParameterSource namedParameters = new MapSqlParameterSource();
        if (!CollectionUtils.isEmpty(parameters)) {
            for (Entry<String, Object> entry : parameters.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (value != null && !value.equals("")) {
                    namedParameters.addValue(key, value);
                }
            }
        }
        return namedParameters;
    }

    /**
     * <p>Description: 数据结构重构，将SQL查询返回的数据重构成  Map<String, Map<String, Object>>格式            </p>
     */
    @SuppressWarnings({ "rawtypes" })
    private List<Object> resultRefactor(List result, String select) {
        logger.debug("result size is :" + result.size());
        List<Object> resultList = new ArrayList<Object>();
        Map<String, Map<String, Object>> resultMap;
        String[] selectColumn = select.split(",");
        
        if (logger.isDebugEnabled()) {
            logger.debug("selectColumn size is :" + selectColumn.length);
        }

        for (Object obReslt : result) {
            resultMap = new HashMap<String, Map<String, Object>>();
            // 处理result返回不为数组的BUG
            if (selectColumn.length == 1) {
                String[] columnBunch = selectColumn[0].trim().split(" ");
                String[] alias = null;
                for (String disassemble : columnBunch) {
                    if (disassemble.indexOf(".") != -1) {
                        alias = disassemble.split("\\.");
                    }
                }
                if (!resultMap.containsKey(alias[0])) {
                    resultMap.put(alias[0], new HashMap<String, Object>());
                }
                ((Map<String, Object>) resultMap.get(alias[0])).put(columnBunch[columnBunch.length - 1], obReslt);

            }
            else {
                for (int i = 0; i < selectColumn.length; i++) {
                    String[] columnBunch = selectColumn[i].trim().split(" ");
                    String[] alias = null;
                    for (String disassemble : columnBunch) {
                        if (disassemble.indexOf(".") != -1) {
                            alias = disassemble.split("\\.");
                        }
                    }
                    if (!resultMap.containsKey(alias[0])) {
                        resultMap.put(alias[0], new HashMap<String, Object>());
                    }
                    ((Map<String, Object>) resultMap.get(alias[0])).put(columnBunch[columnBunch.length - 1],
                            ((Object[]) obReslt)[i]);
                }
            }
            resultList.add(resultMap);
        }
        return resultList;
    }

    public void setNamedParameterJdbcTemplate(NamedParameterJdbcOperations namedParameterJdbcTemplate) {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }
}
